package cn.lichenfei.fxui.controls;

import cn.lichenfei.fxui.common.FxUtil;
import cn.lichenfei.fxui.common.Level;
import cn.lichenfei.fxui.common.SimpleControl;
import javafx.animation.*;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.geometry.Rectangle2D;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.*;
import javafx.scene.text.Text;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import javafx.stage.Window;
import javafx.util.Duration;
import org.kordamp.ikonli.antdesignicons.AntDesignIconsOutlined;
import org.kordamp.ikonli.javafx.FontIcon;

import java.util.ArrayList;
import java.util.List;

public class CFNotification extends Stage {

    private static final String ROOT_STYLE_SHEET = FxUtil.getResource("/css/cf-base.css");
    private static final String STYLE_SHEET = FxUtil.getResource("/css/cf-notification.css");
    private static final Stage UTILITY_STAGE = new Stage();
    private static final ObservableList<CFNotification> STAGES = FXCollections.observableArrayList(); // 展示的窗口集合

    static {
        UTILITY_STAGE.setScene(new Scene(new StackPane(), 1, 1));
        UTILITY_STAGE.setX(Double.MAX_VALUE);
        UTILITY_STAGE.initStyle(StageStyle.UTILITY);
        STAGES.addListener((ListChangeListener<CFNotification>) change -> {
            if (!change.next()) {
                return;
            }
            List<? extends CFNotification> removed = change.getRemoved();
            if (removed.isEmpty()) {
                return;
            }
            CFNotification cfNotification = removed.get(removed.size() - 1);
            int from = change.getFrom();
            System.out.println(from);
            List<CFNotification> subList = new ArrayList<>(STAGES.subList(from, STAGES.size()));
            Double[] yArray = subList.stream().map(Window::getY).toArray(Double[]::new);
            // 循环处理窗口高度动画
            cfNotification.BUTTON.translateYProperty().addListener((observable, oldValue, newValue) -> {
                for (int i = 0; i < yArray.length; i++) {
                    subList.get(i).setY(yArray[i] + newValue.doubleValue());
                }
            });
        });
    }

    //
    private final ObjectProperty<Level> level = new SimpleObjectProperty<>(Level.PRIMARY);

    /******************************* 节点 *******************************/

    private final Label title = new Label("Title");
    private final Text close = FontIcon.of(AntDesignIconsOutlined.CLOSE, 30);
    private final HBox header = new HBox(title, close);
    private final BorderPane container = new BorderPane(null, header, null, null, null);
    private final FontIcon icon = FontIcon.of(level.get().getIkon());
    private final HBox root = new HBox(icon, container);

    /******************************* 动画 *******************************/

    private final Button BUTTON = new Button();
    //显示动画
    private final TranslateTransition SHOW_XT = new TranslateTransition(Duration.millis(250), BUTTON);
    private final PauseTransition SHOW_PT = new PauseTransition(Duration.seconds(5));
    private final SequentialTransition SHOW_ST = new SequentialTransition(SHOW_XT, SHOW_PT);
    //隐藏动画
    private final FadeTransition HIDE_FT = new FadeTransition(Duration.millis(250), root);
    private final TranslateTransition HIDE_YT = new TranslateTransition(Duration.millis(250), BUTTON);
    private final ParallelTransition HIDE_PT = new ParallelTransition(HIDE_FT, HIDE_YT);

    public static CFNotification create(String text) {
        return new CFNotification().setCenterText(text);
    }

    /**
     * 窗口停留时间
     */
    public CFNotification setDuration(Duration duration) {
        SHOW_PT.setDuration(duration);
        return this;
    }

    public CFNotification setLevel(Level val) {
        level.set(val);
        return this;
    }

    public CFNotification setBottom(Node node) {
        container.setBottom(node);
        return this;
    }

    public CFNotification setCenter(Node node) {
        container.setCenter(node);
        return this;
    }

    public CFNotification setTitleText(String text) {
        title.setText(text);
        return this;
    }

    public CFNotification setCenterText(String text) {
        container.setCenter(new StackPane(SimpleControl.getLabel(text)));
        return this;
    }

    private CFNotification() {
        initialize();
    }

    private void initialize() {
        title.setMaxSize(Double.MAX_VALUE, Region.USE_PREF_SIZE);
        HBox.setHgrow(title, Priority.ALWAYS);
        HBox.setHgrow(container, Priority.ALWAYS);
        //
        title.getStyleClass().add("title");
        close.getStyleClass().add("close");
        header.getStyleClass().add("header");
        icon.getStyleClass().add("icon");
        container.getStyleClass().add("container");
        container.centerProperty().addListener((observable, oldValue, newValue) -> {
            if (newValue != null) {
                newValue.getStyleClass().add("center");
            }
        });
        root.getStyleClass().add("cf-notification");
        // 重置styleClass,icon
        level.addListener((observable, oldValue, newValue) -> {
            icon.setIconCode(level.get().getIkon());
            newValue.resetStyleClass(root);
        });
        root.setOpacity(0);
        initOwner(UTILITY_STAGE);
        initStyle(StageStyle.TRANSPARENT);
        setAlwaysOnTop(true);
        Scene scene = new Scene(root);
        scene.setFill(null);
        scene.getStylesheets().addAll(ROOT_STYLE_SHEET, STYLE_SHEET);
        setScene(scene);
        //
        initEvent();
    }

    private void initEvent() {
        if (!UTILITY_STAGE.isShowing()) {
            UTILITY_STAGE.show();
        }
        // 显示窗口后展示
        setOnShown(event -> {
            double height = getHeight();
            // 重新设置窗口坐标
            Rectangle2D vb = Screen.getPrimary().getVisualBounds();
            setX(vb.getMaxX());
            if (STAGES.isEmpty()) {
                setY(vb.getMaxY() - height);
            } else {
                CFNotification cn = STAGES.get(STAGES.size() - 1);
                setY(cn.getY() - height);
            }
            STAGES.add(this);
            //开始动画
            play();
            root.setOpacity(1);
        });
        // 手动关闭
        close.setOnMouseClicked(event -> hide());
        root.hoverProperty().addListener((observable, oldValue, newValue) -> {
            if (newValue) {// 鼠标移入暂停动画
                SHOW_ST.pause();
            } else {// 鼠标移出启动动画
                SHOW_ST.play();
            }
        });
    }

    // 启动动画
    private void play() {
        // 设置动画属性
        BUTTON.translateXProperty().addListener((observable, oldValue, newValue) -> setX(newValue.doubleValue()));
        SHOW_XT.setFromX(getX());
        SHOW_XT.setToX(getX() - getWidth());
        SHOW_XT.setInterpolator(Interpolator.LINEAR);
        SHOW_ST.setOnFinished(event -> hide()); // 隐藏
        SHOW_ST.play(); // 开始动画
    }

    @Override
    public void hide() {
        STAGES.remove(this);
        HIDE_YT.setFromY(0);
        HIDE_YT.setToY(getHeight());
        HIDE_YT.setInterpolator(Interpolator.LINEAR);
        HIDE_FT.setFromValue(1);
        HIDE_FT.setToValue(0);
        HIDE_PT.setOnFinished(event -> {
            SHOW_ST.stop();
            super.hide();
        });
        HIDE_PT.play();
    }
}
